package cn.ytr.crm.shiro;

import cn.ytr.crm.domain.Employee;
import cn.ytr.crm.mapper.EmployeeMapper;
import cn.ytr.crm.mapper.PermissionMapper;
import cn.ytr.crm.mapper.RoleMapper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * CRM 项目 shiro 的自定义数据源,用来提供数据的
 */
@Component("CRMRealm")
public class CRMRealm extends AuthorizingRealm{

    @Autowired
    private EmployeeMapper employeeMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private RoleMapper roleMapper;

    //要给 compoment 注入属性，就给属性 set 方法贴 @Autowired 注解
    @Autowired
    //配置密码匹配器
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        super.setCredentialsMatcher(credentialsMatcher);
    }

    /**
     * 提供用户认证的数据（认证，登录）
     * 该方法直接返回 info 就行，shiro 底层会有判断的
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //获取 token 令牌中的用户名（token 来自于登录时用户填写的，已经在 authenticationToken 容器中）
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();

        System.out.println("登录输入的 username 为：" + username);
        System.out.println("登录输入的 password 为：" + String.valueOf(token.getPassword()));

        if (employeeMapper != null) {
            //判断该用户名是否在数据库中
            Employee employee = employeeMapper.selectByName(username);
            if (employee != null) {
                /* SimpleAuthenticationInfo()
                 * 第一参是身份用户填写的身份信息，可以直接传用户名，也可以传某个对象，subject.getPrincipal() 获取的身份信息就是这里的值
                 * 第二参是真实的密码，第四参是要查询的数据源(该数据源是一个标志，以后可能会有不同的数据源)
                 * 第三参是密码加密用到的盐，加盐是 MD5(username+password) 格式
                 * 该方法会判断认证数据和数据源的密码是否匹配，然后返回 info 后， shiro 底层会帮我们抛出异常
                 */
                return new SimpleAuthenticationInfo(employee, employee.getPassword(), ByteSource.Util.bytes(username), "CRMRealm");
            }
        } else {
            //如果 employeeMapper 为空，说明没有用 spring，这是在做 test，下面就是模拟的数据源
            //虚拟的用户的真实密码
            String passwordTest = "121";
            //生成 info，实际上就是比对密码
            return new SimpleAuthenticationInfo(username, passwordTest, ByteSource.Util.bytes(username),"CRMRealm");
        }

        //返回 null 表示账号不存在
        return null;
    }

    /**
     * 提供用户的权限数据（授权）返回的 info 包含当前主体的角色和权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("===数据源给主体授权===");

        //创建一个用于返回的简单型的授权信息对象
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获取访问主体
        Subject subject = SecurityUtils.getSubject();
        //获取身份信息 employee 对象
        Employee employee = (Employee)subject.getPrincipal();
        //由于员工超级管理员的属性不是权限，不会被权限缓存刷新而刷新，所以这里直接从数据库查
        Employee employeeBySql = employeeMapper.selectByName(employee.getName());

        //判断是否超管
        if (employeeBySql.getAdmin()) {
            //给主体分配一个超管角色 ADMIN
            info.addRole("ADMIN");
            //通过通配符给主体所有权限
            info.addStringPermission("*:*");
        } else {
            //查询用户拥有的权限
            List<String> permissions = permissionMapper.selectByEmpId(employee.getId());
            //给主体注入权限
            info.addStringPermissions(permissions);
            //查询用户拥有的角色
            List<String> roles = roleMapper.selectStingListByEmpId(employee.getId());
            //给主体注入角色
            info.addRoles(roles);
        }

        return info;
    }

    /*//清除当前主体的缓存
    public void clearCached() {
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        super.clearCache(principals);
    }*/

}


































